<?php
/**
 * crcl/base
 *
 * Copyright 2013 CRCL - André Sabosch - info@crcl.de
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace crcl\base\mvc;

interface IValues
{
  /**
   * Returns values instance from doc comment (factory)
   * used for in values. sets outs of doc comment for further use as static var
   *
   * @param \ReflectionMethod $oMethod
   *
   * @return \crcl\base\mvc\Values
   */
  public static function getInstanceFromDocComment(\ReflectionMethod $oMethod);

  /**
   * Returns values instance from array (factory)
   * used for out values. uses doc comment set by getInstanceFromDocComments
   *
   * @param array $aValues
   */
  public static function getInstanceFromArray($aValues);

  /**
   * Returns value with passed name
   *
   * @param string $sName
   * @return \crcl\base\mvc\value\IConcrete $oValue
   */
  public function getValue($sName);

  /**
   * Adds new Value Object. Overrides if name already exists
   *
   * @param string  $sName Name of value
   * @param \crcl\base\mvc\value\IConcrete $oValue
   */
  public function putValue($sName, \crcl\base\mvc\value\IConcrete $oValue);

  /**
   * Returns array of all values: name => value
   *
   * @return  array
   */
  public function toArray();
}

class Values implements IValues
{
  public static function getInstanceFromDocComment(\ReflectionMethod $oMethod)
  {
    $aUrns = self::getUrnsFromDocComment($oMethod->getDocComment());
    $oValues = new Values();

    foreach ($aUrns as $sName => $sUrn)
    {
      $oValue = Value::getInstance($sUrn);
      $oValues->putValue($sName, $oValue);
    }
    return $oValues;
  }

  public static function getInstanceFromArray($aValues)
  {
    if (!\is_array($aValues))
    {
      $aValues = (array)$aValues;
    }
    $oValues = new Values();
    $i = 0;

    foreach (self::$aDocComment as $sName => $sUrn)
    {
      $oValue = Value::getInstance($sUrn);
      if (isset($aValues[$sName]))
      {
        $oValue->set($aValues[$sName]);
      }
      else
      {
        if (isset($aValues[$i]))
        {
          $oValue->set($aValues[$i]);
        }
      }
//      $oValue->set($aValueNames[$sName]);

      $oValues->putValue($sName, $oValue);
      $i++;
    }
    return $oValues;
  }

  public function putValue($sName, \crcl\base\mvc\value\IConcrete $oValue)
  {
    $this->aValues[$sName] = $oValue;
  }

  public function getValue($sName)
  {
    if (isset($this->aValues[$sName]))
    {
      return $this->aValues[$sName];
    }
    throw new Exception(
      Exception::ERROR_VALUES_NOTFOUND,
      array('name' => $sName, 'values' => $this->aValues));
  }

  public function toArray()
  {
    $aResult = array();
    if ($this->aValues != array())
    {
      foreach ($this->aValues as $sName => $oValue)
      {
        $aResult[$sName] = $oValue->get();
      }
    }
    return $aResult;
  }

  /** PRIVATE *************************************************************************************/

  private static $aDocComment = array();

  private $aValues = array();

  private static function getUrnsFromDocComment($sComment)
  {
    if ($sComment == '')
    {
      return array();
    }
    $sComment = \crcl\base\String::getInstance($sComment)
      ->doNotStartWith('/**')
      ->doNotEndWith('*/');
    $sComment = \str_replace('*', '', $sComment);
    $sComment = \trim(\preg_replace('/\s+/', ' ', $sComment));
    $sComment = \crcl\base\String::getInstance($sComment)->doStartWith('@');

    $aComment = explode('@', $sComment);
    unset($aComment[0]);

    $aResult = array();
    $aReturn = array();
    foreach ($aComment as $sLine)
    {
      $sLine = \trim($sLine);
      $sLine = \str_replace(' ', ':', $sLine);

      $aUrn = \str_getcsv($sLine, ':', '"');

      if ($aUrn[0] == 'out')
      {
        $aResult[$aUrn[1]] = $sLine;
      }
      if ($aUrn[0] == 'in')
      {
        $aReturn[$aUrn[1]] = $sLine;
      }
    }

    self::$aDocComment = $aResult;
    return $aReturn;
  }
}